package com.ansion.commonchat.startup.workmanager.bwork;

import android.annotation.SuppressLint;
import android.content.Context;

import androidx.annotation.Keep;
import androidx.annotation.MainThread;
import androidx.annotation.NonNull;

import com.ansion.basedb.dao.model.UnProguard;
import com.ansion.commonchat.startup.workmanager.bwork.utils.taskexecutor.BTaskExecutor;
import com.ansion.exception.ExceptionExtension;
import com.google.common.util.concurrent.ListenableFuture;

import java.util.Set;
import java.util.UUID;
import java.util.concurrent.Executor;

/**
 * Created by wanghongjie on 2023/5/11
 * Describe : 用于定义和执行后台工作任务，并提供了对工作执行状态的监听能力。
 * 它是创建自定义后台工作的基类。
 */
public abstract class BListenableWorker implements UnProguard {

    private @NonNull
    Context mAppContext;
    private @NonNull
    BWorkerParameters mWorkerParams;

    private volatile boolean mStopped;

    private boolean mUsed;

    @Keep
    @SuppressLint("BanKeepAnnotation")
    public BListenableWorker(@NonNull Context appContext, @NonNull BWorkerParameters workerParams) {
        if (appContext == null) {
            ExceptionExtension.throwThrowable(new IllegalArgumentException("Application Context is null"));
        }

        if (workerParams == null) {
            ExceptionExtension.throwThrowable(new IllegalArgumentException("BWorkerParameters is null"));
        }

        mAppContext = appContext;
        mWorkerParams = workerParams;
    }

    public final @NonNull
    Context getApplicationContext() {
        return mAppContext;
    }

    public final @NonNull
    UUID getId() {
        return mWorkerParams.getId();
    }

    public final @NonNull
    BData getInputData() {
        return mWorkerParams.getInputData();
    }

    public final @NonNull
    Set<String> getTags() {
        return mWorkerParams.getTags();
    }

    @MainThread
    public abstract @NonNull
    ListenableFuture<Result> startWork();

    public final boolean isStopped() {
        return mStopped;
    }

    public final void stop() {
        mStopped = true;
        onStopped();
    }

    public void onStopped() {
    }

    public final boolean isUsed() {
        return mUsed;
    }

    public final void setUsed() {
        mUsed = true;
    }

    public @NonNull
    Executor getBackgroundExecutor() {
        return mWorkerParams.getBackgroundExecutor();
    }

    public @NonNull
    BTaskExecutor getTaskExecutor() {
        return mWorkerParams.getTaskExecutor();
    }

    public abstract static class Result {
        @NonNull
        public static Result success() {
            return new Success();
        }

        @NonNull
        public static Result success(@NonNull BData outputData) {
            return new Success(outputData);
        }

        @NonNull
        public static Result retry() {
            return new Retry();
        }

        @NonNull
        public static Result failure() {
            return new Failure();
        }

        @NonNull
        public static Result failure(@NonNull BData outputData) {
            return new Failure(outputData);
        }

        @NonNull
        public abstract BData getOutputData();

        Result() {
        }

        public static final class Success extends Result {
            private final BData mOutputData;

            public Success() {
                this(BData.EMPTY);
            }

            public Success(@NonNull BData outputData) {
                super();
                mOutputData = outputData;
            }

            @Override
            public @NonNull
            BData getOutputData() {
                return mOutputData;
            }

            @Override
            public boolean equals(Object o) {
                if (this == o) return true;
                if (o == null || getClass() != o.getClass()) return false;

                Success success = (Success) o;

                return mOutputData.equals(success.mOutputData);
            }

            @Override
            public int hashCode() {
                String name = Success.class.getName();
                return 31 * name.hashCode() + mOutputData.hashCode();
            }

            @Override
            public String toString() {
                return "Success {" + "mOutputData=" + mOutputData + '}';
            }
        }

        public static final class Failure extends Result {
            private final BData mOutputData;

            public Failure() {
                this(BData.EMPTY);
            }

            public Failure(@NonNull BData outputData) {
                super();
                mOutputData = outputData;
            }

            @Override
            public @NonNull
            BData getOutputData() {
                return mOutputData;
            }

            @Override
            public boolean equals(Object o) {
                if (this == o) return true;
                if (o == null || getClass() != o.getClass()) return false;

                Failure failure = (Failure) o;

                return mOutputData.equals(failure.mOutputData);
            }

            @Override
            public int hashCode() {
                String name = Failure.class.getName();
                return 31 * name.hashCode() + mOutputData.hashCode();
            }

            @Override
            public String toString() {
                return "Failure {" + "mOutputData=" + mOutputData + '}';
            }
        }

        public static final class Retry extends Result {
            public Retry() {
                super();
            }

            @Override
            public boolean equals(Object o) {
                if (this == o) return true;
                return o != null && getClass() == o.getClass();
            }

            @Override
            public int hashCode() {
                String name = Retry.class.getName();
                return name.hashCode();
            }

            @NonNull
            @Override
            public BData getOutputData() {
                return BData.EMPTY;
            }

            @Override
            public String toString() {
                return "Retry";
            }
        }
    }
}